package cn.demoncat.util.web;

import cn.demoncat.util.exception.RestRuntimeException;
import cn.demoncat.util.io.CloseUtil;
import cn.demoncat.util.lang.CollectionUtil;
import cn.demoncat.util.lang.LogUtil;
import cn.demoncat.util.lang.StringUtil;
import cn.demoncat.util.lang.constant.CharsetConstant;
import cn.demoncat.util.lang.entity.Result;
import cn.demoncat.util.web.constant.HttpConstant;
import cn.demoncat.util.web.entity.HttpParam;
import org.apache.commons.collections4.MapUtils;
import org.apache.http.Consts;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
 
/**
 * Http 客户端请求工具
 *
 * @author yxl
 *
 * @version 2.0
 *
 * @since 2016年8月11日
 */
public final class HttpClientUtil {

	private static final Logger LOG = LogUtil.get(HttpClientUtil.class);
	
	/**
	 * ContentType - XML
	 */
	private static final ContentType CT_XML_UTF_8 = ContentType.create(ContentType.APPLICATION_XML.getMimeType(), Consts.UTF_8);

	/**
	 * 获取Redirect地址
	 *
	 * 1、发起请求，返回302
	 * 2、响应时，将302的location头封装为 Result.redirectTop(url)
	 * 3、通过判断是否为Result.redirectTop(url)获取重定向地址，空表示没有重定向
	 *
	 * @param response 响应结果字符串
	 *
	 * @return 重定向地址，未重定向返回null
	 *
	 * @author 延晓磊
	 * @since 2021年03月01日
	 */
	public static String getRedirectUrl(String response){
		if (response.contains("\"status\":\"320\"")) {
			// 重定向Result JSON
			try {
				// 获取重定向地址
				return Result.parseJson(response).getStr();
			}catch (Exception e){
				return null;
			}
		}
		return null;
	}

	/**
	 * POST-REST请求
	 * 
	 * @param url		请求地址
	 * @return	结果Result
	 * 
	 * @author 延晓磊
	 *
	 * @since 2018年5月17日
	 */
	public static Result restPost(String url) {
		return HttpClientUtil.result(HttpClientUtil.doPost(url));
	}
	
	/**
	 * POST-REST请求
	 * 
	 * @param url		请求地址
	 * @param paramName	单参名
	 * @param paramVal	单参值
	 * 
	 * @return	结果Result
	 * 
	 * @author 延晓磊
	 *
	 * @since 2018年5月17日
	 */
	public static Result restPost(String url,String paramName,String paramVal) {
		Map<String, String> params = new HashMap<>(1);
		params.put(paramName, paramVal);
		return HttpClientUtil.result(HttpClientUtil.doPost(url, params));
	}
	
	/**
	 * POST-REST请求
	 * 
	 * @param url		请求地址
	 * @param params	请求参数
	 * 
	 * @return	结果Result
	 * 
	 * @author 延晓磊
	 *
	 * @since 2018年5月17日
	 */
	public static Result restPost(String url,Map<String, String> params) {
		return HttpClientUtil.result(HttpClientUtil.doPost(url, params));
	}
	
	/**
	 * POST-REST请求
	 * 
	 * @param url		请求地址
	 * @param params	请求参数
	 * @param headers	请求头
	 * 
	 * @return	结果Result
	 * 
	 * @author 延晓磊
	 *
	 * @since 2018年5月17日
	 */
	public static Result restPost(String url,Map<String, String> params, Map<String, String> headers) {
		return HttpClientUtil.result(HttpClientUtil.doPost(url, params,headers));
	} 
	
	/**
	 * GET-REST请求
	 * 
	 * @param url		请求地址
	 * @return	结果Result
	 * 
	 * @author 延晓磊
	 *
	 * @since 2018年5月17日
	 */
	public static Result restGet(String url) {
		return HttpClientUtil.result(HttpClientUtil.doGet(url));
	}
	
	/**
	 * GET-REST请求
	 * 
	 * @param url		请求地址
	 * @param paramName	单参名
	 * @param paramVal	单参值
	 * 
	 * @return	结果Result
	 * 
	 * @author 延晓磊
	 *
	 * @since 2018年5月17日
	 */
	public static Result restGet(String url,String paramName,String paramVal) {
		Map<String, String> params = new HashMap<>(1);
		params.put(paramName, paramVal);
		return HttpClientUtil.result(HttpClientUtil.doGet(url, params));
	}
	
	/**
	 * GET-REST请求
	 * 
	 * @param url		请求地址
	 * @param params	请求参数
	 * 
	 * @return	结果Result
	 * 
	 * @author 延晓磊
	 *
	 * @since 2018年5月17日
	 */
	public static Result restGet(String url,Map<String, String> params) {
		return HttpClientUtil.result(HttpClientUtil.doGet(url, params));
	}
	
	/**
	 * GET-REST请求
	 * 
	 * @param url		请求地址
	 * @param params	请求参数
	 * @param headers	请求头
	 * 
	 * @return	结果Result
	 * 
	 * @author 延晓磊
	 *
	 * @since 2018年5月17日
	 */
	public static Result restGet(String url,Map<String, String> params, Map<String, String> headers) {
		return HttpClientUtil.result(HttpClientUtil.doGet(url, params,headers));
	}
	
	/**
	 * POST-JSON-REST请求
	 * 
	 * @param url		请求地址
	 * @param json		请求JSON，服务端需用@RequestBody接收对象参数
	 * @param headers	请求头
	 * 
	 * @return	结果Result
	 * 
	 * @author 延晓磊
	 *
	 * @since 2018年5月17日
	 */
	public static Result restPostJson(String url,String json, Map<String, String> headers) {
		return HttpClientUtil.result(HttpClientUtil.doPostJson(url, json,headers));
	} 
	
	/**
	 * POST-JSON-REST请求
	 * 
	 * @param url		请求地址
	 * @param json		请求JSON，服务端需用@RequestBody接收对象参数
	 * 
	 * @return	结果Result
	 * 
	 * @author 延晓磊
	 *
	 * @since 2018年5月17日
	 */
	public static Result restPostJson(String url,String json) {
		return HttpClientUtil.result(HttpClientUtil.doPostJson(url, json));
	}
	
	/**
	 * 返回Result结果
	 * 
	 * @param result	Http调用返回的结果(被调用方返回Reult的JSON串)
	 * 
	 * @return	转换为Result并校验：如果为200则返回Result；如果非200会抛异常；如果希望报400警告错误时不抛异常，可以try-cache异常BizRuntimeException
	 * 
	 * @author 延晓磊
	 *
	 * @since 2018年5月17日
	 */
	public static Result result(String result) {
		return Result.rest(Result.parseJson(result));
	}
	
	/**
	 * JSON格式的POST请求：如果返回200则获取响应体；如果返回302则返回{status:"320",msg:"重定向页面",str:"Location头信息"}，以便进行手动重定向
	 * 
	 * 注：，服务端需用@RequestBody接收对象参数
	 * 
	 * @throws RestRuntimeException 
	 */
	public static String doPostJson(String url, String json){
		return doPostJson(url, json,null);
	}

	/**
	 * JSON格式的POST请求，带请求头
	 * 
	 * 注：服务端需用@RequestBody接收对象参数
	 * 
	 * @throws RestRuntimeException 
	 */
	public static String doPostJson(String url, String json, Map<String, String> headers)  {
		return doPostJson(url, json, null, headers);
	}

	/**
	 * JSON格式的POST请求，带请求头
	 *
	 * 注：服务端需用@RequestBody接收对象参数
	 *
	 * @throws RestRuntimeException
	 */
	public static String doPostJson(String url, String json, Map<String, String> params, Map<String, String> headers)  {
		LOG.info(HttpConstant.HTTP_SEND_LOG, HttpConstant.Method.POST, url);
		CloseableHttpClient httpClient = null;
		CloseableHttpResponse response = null;
		// 创建Http Post请求，拼接query参数
		HttpPost httpPost = new HttpPost(UrlUtil.toUrl(UrlUtil.trimHash(url), params));
		try {
			// 创建Httpclient对象
			httpClient = HttpClients.createDefault();
			// 添加请求头
			setHeaders(httpPost, headers);
			// 添加请求内容
			httpPost.setEntity(new StringEntity(json, ContentType.APPLICATION_JSON));
			// 执行http请求
			response = httpClient.execute(httpPost);
			// 返回结果
			return getResponse(response,url);
		}  catch (Exception e) {
			throw RestRuntimeException.httpUrlError(url , e);
		} finally{
			CloseUtil.close(httpClient);
			CloseUtil.close(response);
		}
	}

	/**
	 * JSON格式的POST请求：如果返回200则获取响应体；如果返回302则返回{status:"320",msg:"重定向页面",str:"Location头信息"}，以便进行手动重定向
	 *
	 * 注：，服务端需用@RequestBody接收对象参数
	 *
	 * @throws RestRuntimeException
	 */
	public static byte[] doPostJsonBytes(String url, String json){
		return doPostJsonBytes(url, json,null);
	}

	/**
	 * JSON格式的POST请求，带请求头
	 *
	 * 注：服务端需用@RequestBody接收对象参数
	 *
	 * @throws RestRuntimeException
	 */
	public static byte[] doPostJsonBytes(String url, String json, Map<String, String> headers)  {
		return doPostJsonBytes(url, json, null, headers);
	}

	/**
	 * JSON格式的POST请求，带请求头
	 *
	 * 注：服务端需用@RequestBody接收对象参数
	 *
	 * @throws RestRuntimeException
	 */
	public static byte[] doPostJsonBytes(String url, String json, Map<String, String> params, Map<String, String> headers)  {
		LOG.info(HttpConstant.HTTP_SEND_LOG, HttpConstant.Method.POST, url);
		CloseableHttpClient httpClient = null;
		CloseableHttpResponse response = null;
		// 创建Http Post请求，拼接query参数
		HttpPost httpPost = new HttpPost(UrlUtil.toUrl(UrlUtil.trimHash(url), params));
		try {
			// 创建Httpclient对象
			httpClient = HttpClients.createDefault();
			// 添加请求头
			setHeaders(httpPost, headers);
			// 添加请求内容
			httpPost.setEntity(new StringEntity(json, ContentType.APPLICATION_JSON));
			// 执行http请求
			response = httpClient.execute(httpPost);
			// 返回结果
			return getResponseBytes(response,url);
		}  catch (Exception e) {
			throw RestRuntimeException.httpUrlError(url , e);
		} finally{
			CloseUtil.close(httpClient);
			CloseUtil.close(response);
		}
	}
	
	/**
	 * XML格式的POST请求
	 * 
	 * @param url
	 * @param xml
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月11日
	 */
	public static String doPostXml(String url, String xml)  {
		return doPostXml(url, xml, null);
	}
	/**
	 * XML格式的POST请求，带请求头
	 * 
	 * @param url
	 * @param xml
	 * @param headers
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年10月11日
	 */
	public static String doPostXml(String url, String xml, Map<String, String> headers)  {
		LOG.info(HttpConstant.HTTP_SEND_LOG, HttpConstant.Method.POST, url);
		CloseableHttpClient httpClient = null;
		CloseableHttpResponse response = null;
		// 创建Http Post请求
		HttpPost httpPost = new HttpPost(UrlUtil.trimHash(url));
		try {
			// 创建Httpclient对象
			httpClient = HttpClients.createDefault();
			// 添加请求头
			setHeaders(httpPost, headers);
			// 添加请求内容
			StringEntity entity = new StringEntity(xml, CT_XML_UTF_8);
			httpPost.setEntity(entity);
			// 执行http请求
			response = httpClient.execute(httpPost);
			// 返回结果
			return getResponse(response,url);
		}  catch (Exception e) {
			throw RestRuntimeException.httpUrlError(url , e);
		} finally{
			CloseUtil.close(httpClient);
			CloseUtil.close(response);
		}
	}
	
	/**
	 * DELETE请求
	 * 
	 * @param url
	 * @return 如果返回200则获取响应体；如果返回302则返回{status:"320",msg:"重定向页面",str:"Location头信息"}，以便进行手动重定向
	 * @throws RestRuntimeException 
	 */
	public static String doDelete(String url) {
		return doDelete(url, null);
	}
	
	/**
	 * 带参数的DELETE请求
	 * 
	 * @param url
	 * @param param
	 * @return 如果返回200则获取响应体；如果返回302则返回{status:"320",msg:"重定向页面",str:"Location头信息"}，以便进行手动重定向
	 * @throws RestRuntimeException 
	 */
	public static String doDelete(String url, Map<String, String> param) {
		return doDelete(url, param, null);
	}
	
	/**
	 * 带参数和请求头的DELETE请求
	 * 
	 * @param url
	 * @param params
	 * @param headers
	 * @return 如果返回200则获取响应体；如果返回302则返回{status:"320",msg:"重定向页面",str:"Location头信息"}，以便进行手动重定向
	 * @throws RestRuntimeException 
	 */
	public static String doDelete(String url, Map<String, String> params, Map<String, String> headers) {
		CloseableHttpClient httpClient = null;
		CloseableHttpResponse response = null;
		try {
			// 创建Httpclient对象
			httpClient = HttpClients.createDefault();
			// 创建Http-DELETE请求
			HttpDelete httpDelete = new HttpDelete(getUrl(HttpConstant.Method.DELETE, url, params));
			// 添加请求头
			setHeaders(httpDelete, headers);
			// 执行请求
			response = httpClient.execute(httpDelete);
			// 返回结果
			return getResponse(response,url);
		} catch (IOException e) {
			throw RestRuntimeException.httpUrlError(url , e);
		} finally{
			CloseUtil.close(httpClient);
			CloseUtil.close(response);
		}
	}
	
	/**
	 * GET请求
	 * 
	 * @param url
	 * @return 如果返回200则获取响应体；如果返回302则返回{status:"320",msg:"重定向页面",str:"Location头信息"}，以便进行手动重定向
	 * @throws RestRuntimeException 
	 */
	public static String doGet(String url) {
		return doGet(url, null);
	}
	
	/**
	 * 带参数的GET请求
	 * 
	 * @param url
	 * @param param
	 * @return 如果返回200则获取响应体；如果返回302则返回{status:"320",msg:"重定向页面",str:"Location头信息"}，以便进行手动重定向
	 * @throws RestRuntimeException 
	 */
	public static String doGet(String url, Map<String, String> param) {
		return doGet(url, param, null);
	}
	
	/**
	 * 带参数和请求头的GET请求
	 * 
	 * @param url
	 * @param params
	 * @param headers
	 * @return 如果返回200则获取响应体；如果返回302则返回{status:"320",msg:"重定向页面",str:"Location头信息"}，以便进行手动重定向
	 * @throws RestRuntimeException 
	 */
	public static String doGet(String url, Map<String, String> params, Map<String, String> headers) {
		CloseableHttpClient httpClient = null;
		CloseableHttpResponse response = null;
		try {
			// 创建Httpclient对象
			httpClient = HttpClients.createDefault();
			// 创建Http-GET请求
			HttpGet httpGet = new HttpGet(getUrl(HttpConstant.Method.GET, url, params));
			// 添加请求头
			setHeaders(httpGet, headers);
			// 执行请求
			response = httpClient.execute(httpGet);
			// 返回结果
			return getResponse(response,url);
		} catch (IOException e) {
			throw RestRuntimeException.httpUrlError(url , e);
		} finally{
			CloseUtil.close(httpClient);
			CloseUtil.close(response);
		}
	}
	
	/**
	 * 带参数和请求头的GET请求
	 * 
	 * @param param
	 * @return 如果返回200则获取响应体；如果返回302则返回{status:"320",msg:"重定向页面",str:"Location头信息"}，以便进行手动重定向
	 * @throws RestRuntimeException 
	 */
	public static String doGet(HttpParam param) {
		 return doGet(param.getUrl(), param.getParams(), param.getHeaders());
	}

	/**
	 * POST请求
	 * 
	 * @param url
	 * @return 如果返回200则获取响应体；如果返回302则返回{status:"320",msg:"重定向页面",str:"Location头信息"}，以便进行手动重定向
	 * @throws RestRuntimeException 
	 */
	public static String doPost(String url){
		return doPost(url, null);
	}
	
	/**
	 * 带参数的POST请求
	 * 
	 * @param url
	 * @param params
	 * @return 如果返回200则获取响应体；如果返回302则返回{status:"320",msg:"重定向页面",str:"Location头信息"}，以便进行手动重定向
	 * @throws RestRuntimeException 
	 */
	public static String doPost(String url, Map<String, String> params){
		return doPost(url, params, null);
	}
	
	/**
	 * 带参数和请求头的POST请求
	 *
	 * <pre>
	 * 请求头(从浏览器请求中复制获取)：
	 *
	 * 1、代理服务器：Host: 代理的网址
	 * 2、防止403禁止访问资源：Referer: 来源的网址
	 * 3、模仿浏览器：User-Agent: Mozilla/5.0(Windows NT 6.1; WOW64; rv:44.0)Gecko/20100101 Firefox/44.0
	 * 4、支持的数据：Accept:星/星
	 * 5、支持的语言：Accept-Language:zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
	 * 6、支持压缩：Accept-Encoding:gzip,deflate 7、支持长连接：Connection: keep-alive
	 * 8、设置Cookie：Cookie: k1=v1; k2=v2
	 * </pre>
	 * @param url
	 * @param params
	 * @param headers
	 * @return 如果返回200则获取响应体
	 * 
	 * @throws RestRuntimeException 
	 */
	public static String doPost(String url, Map<String, String> params, Map<String, String> headers) {
		LOG.info(HttpConstant.HTTP_SEND_LOG, HttpConstant.Method.POST, url);
		CloseableHttpClient httpClient = null;
		CloseableHttpResponse response = null;
		// 创建Http Post请求
		HttpPost httpPost = new HttpPost(UrlUtil.trimHash(url));
		try {
			// 创建Httpclient对象
			httpClient = HttpClients.createDefault();
			// 添加请求头
			setHeaders(httpPost, headers);
			// 添加请求参数
			setParams(httpPost, params);
			// 执行http请求
			response = httpClient.execute(httpPost);
			// 返回结果
			return getResponse(response,url);
		}catch (IOException e) {
			throw RestRuntimeException.httpUrlError(url , e);
		}finally{
			CloseUtil.close(httpClient);
			CloseUtil.close(response);
		}
	}
	
	/**
	 * 带参数和请求头的POST请求
	 * 
	 * @param param
	 *
	 * @return 如果返回200则获取响应体；如果返回302则返回{status:"320",msg:"重定向页面",str:"Location头信息"}，以便进行手动重定向
	 * @throws RestRuntimeException 
	 */
	public static String doPost(HttpParam param) {
		 return doPost(param.getUrl(), param.getParams(), param.getHeaders());
	}
	
	/**
	 * GET请求，返回值为byte[]
	 * 
	 * 场景：文件下载、非UTF-8文本
	 * 
	 * @param url
	 * 
	 * @return 如果返回200则获取byte[]
	 * 
	 * @throws RestRuntimeException 
	 */
	public static byte[] doGetBytes(String url) {
		return doGetBytes(url,null);
	}
	
	/**
	 * 带参数的GET请求，返回值为byte[]
	 * 
	 * 场景：文件下载、非UTF-8文本
	 * 
	 * @param url
	 * @param params
	 * 
	 * @return 如果返回200则获取byte[]
	 * 
	 * @throws RestRuntimeException 
	 */
	public static byte[] doGetBytes(String url, Map<String, String> params) {
		return doGetBytes(url, params,null);
	}
	
	/**
	 * 带参数和请求头的GET请求，返回值为byte[]
	 * 
	 * 场景：文件下载、非UTF-8文本
	 * 
	 * @param url
	 * @param params
	 * @param headers
	 * 
	 * @return 如果返回200则获取byte[]
	 * 
	 * @throws RestRuntimeException 
	 */
	public static byte[] doGetBytes(String url, Map<String, String> params, Map<String, String> headers) {
		CloseableHttpClient httpClient = null;
		CloseableHttpResponse response = null;
		try {
			// 创建Httpclient对象
			httpClient = HttpClients.createDefault();
			// 创建Http-GET请求
			HttpGet httpGet = new HttpGet(getUrl(HttpConstant.Method.GET, url, params));
			// 添加请求头
			setHeaders(httpGet, headers);
			// 执行请求
			response = httpClient.execute(httpGet);
			// 返回结果
			return getResponseBytes(response,url);
		} catch (IOException e) {
			throw RestRuntimeException.httpUrlError(url , e);
		} finally{
			CloseUtil.close(httpClient);
			CloseUtil.close(response);
		}
	}
	
	/**
	 * 带参数的POST请求，返回值为byte[]
	 * 
	 * 场景：文件下载、非UTF-8文本
	 * 
	 * @param url
	 * 
	 * @return 如果返回200则获取byte[]
	 * 
	 * @throws RestRuntimeException 
	 */
	public static byte[] doPostBytes(String url) {
		return doPostBytes(url,null);
	}
	
	/**
	 * POST请求，返回值为byte[]
	 * 
	 * 场景：文件下载、非UTF-8文本
	 * 
	 * @param url
	 * @param params
	 * 
	 * @return 如果返回200则获取byte[]
	 * 
	 * @throws RestRuntimeException 
	 */
	public static byte[] doPostBytes(String url, Map<String, String> params) {
		return doPostBytes(url, params,null);
	}
	
	/**
	 * 带参数和请求头的POST请求，返回值为byte[]
	 * 
	 * 场景：文件下载、非UTF-8文本
	 * 
	 * @param url
	 * @param params
	 * @param headers
	 * 
	 * @return 如果返回200则获取byte[]
	 * 
	 * @throws RestRuntimeException 
	 */
	public static byte[] doPostBytes(String url, Map<String, String> params, Map<String, String> headers) {
		LOG.info(HttpConstant.HTTP_SEND_LOG, HttpConstant.Method.POST, url);
		CloseableHttpClient httpClient = null;
		CloseableHttpResponse response = null;
		// 创建Http Post请求
		HttpPost httpPost = new HttpPost(UrlUtil.trimHash(url));
		try {
			// 创建Httpclient对象
			httpClient = HttpClients.createDefault();
			// 添加请求头
			setHeaders(httpPost, headers);
			// 添加请求参数
			setParams(httpPost, params);
			// 执行http请求
			response = httpClient.execute(httpPost);
			// 返回结果
			return getResponseBytes(response,url);
		}catch (IOException e) {
			throw RestRuntimeException.httpUrlError(url , e);
		}finally{
			CloseUtil.close(httpClient);
			CloseUtil.close(response);
		}
	}

	/**
	 * 上传文件
	 *
	 * @param url
	 * @param file		文件，参数名为file
	 * @param fileName	文件名
	 * @param params	请求参数
	 * @param headers	请求头
	 *
	 * @return 如果返回200则获取响应体
	 *
	 * @throws RestRuntimeException
	 */
	public static String upload(String url, byte[] file, String fileName, Map<String, String> params, Map<String, String> headers) {
		LOG.info(HttpConstant.HTTP_SEND_LOG, HttpConstant.Method.POST, url);
		CloseableHttpClient httpClient = null;
		CloseableHttpResponse response = null;
		// 创建Http Post请求
		HttpPost httpPost = new HttpPost(UrlUtil.trimHash(url));
		try {
			// 创建Httpclient对象
			httpClient = HttpClients.createDefault();
			// 添加请求头
			setHeaders(httpPost, headers);
			// 添加请求参数
			MultipartEntityBuilder builder = MultipartEntityBuilder.create();
			builder.addBinaryBody("file", file, ContentType.APPLICATION_OCTET_STREAM, fileName);
			if (MapUtils.isNotEmpty(params)) {
				// 普通参数
				for (String p : params.keySet()) {
					builder.addTextBody(p, params.get(p));
				}
			}
			httpPost.setEntity(builder.build());
			// 执行http请求
			response = httpClient.execute(httpPost);
			// 返回结果
			return getResponse(response,url);
		}catch (IOException e) {
			throw RestRuntimeException.httpUrlError(url , e);
		}finally{
			CloseUtil.close(httpClient);
			CloseUtil.close(response);
		}
	}

	/**
	 * 上传文件
	 *
	 * @param url
	 * @param file		文件，参数名为file
	 * @param fileName	文件名
	 * @param params	请求参数
	 *
	 * @return 如果返回200则获取响应体
	 *
	 * @throws RestRuntimeException
	 */
	public static String upload(String url, byte[] file, String fileName, Map<String, String> params) {
		return upload(url, file, fileName, params, null);
	}

	/**
	 * 设置Get请求头
	 * 
	 * @param httpGet
	 * @param headers
	 */
	private static void setHeaders(HttpGet httpGet, Map<String, String> headers) {
		if (CollectionUtil.isNotEmpty(headers)) {
			//添加请求头
			String value;
			for (Entry<String, String> entry : headers.entrySet()) {
				value = entry.getValue();
				if (StringUtil.isNotNull(value)) {
					httpGet.addHeader(entry.getKey(), value.trim());
				}
			}
		}
	}
	
	/**
	 * 设置Get请求参数
	 * 
	 * @param builder
	 * @param params
	 */
	private static void setParams(URIBuilder builder, Map<String, String> params) {
		if (CollectionUtil.isNotEmpty(params)) {
			//添加参数
			String value;
			for (Entry<String, String> entry : params.entrySet()) {
				value = entry.getValue();
				if (StringUtil.isNotNull(value)) {
					builder.addParameter(entry.getKey(),value.trim());
				}
			}
		}
	}
	
	/**
	 * 设置Post请求头
	 * 
	 * @param httpPost
	 * @param headers
	 */
	private static void setHeaders(HttpPost httpPost, Map<String, String> headers) {
		if (CollectionUtil.isNotEmpty(headers)) {
			//添加请求头
			String value;
			for (Entry<String, String> entry : headers.entrySet()) {
				value = entry.getValue();
				if (StringUtil.isNotNull(value)) {
					httpPost.addHeader(entry.getKey(), value.trim());
				}
			}
		}
	}
	
	/**
	 * 设置POST请求参数
	 * 
	 * @param httpPost
	 * @param params
	 * @throws UnsupportedEncodingException
	 */
	private static void setParams(HttpPost httpPost, Map<String, String> params){
		if (CollectionUtil.isNotEmpty(params)) {
			// 参数的键值对
			List<NameValuePair> paramList = new ArrayList<>();
			String value;
			for (Entry<String, String> entry : params.entrySet()) {
				value = entry.getValue();
				if (StringUtil.isNotNull(value)) {
					paramList.add(new BasicNameValuePair(entry.getKey(), value.trim()));
				}
			}
			// 模拟表单
			UrlEncodedFormEntity entity = null;
			try {
				entity = new UrlEncodedFormEntity(paramList, CharsetConstant.UTF8);
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			}
			httpPost.setEntity(entity);
		}
	}
	
	/**
	 * 设置DELETE请求头
	 * 
	 * @param httpDelete
	 * @param headers
	 */
	private static void setHeaders(HttpDelete httpDelete, Map<String, String> headers) {
		if (CollectionUtil.isNotEmpty(headers)) {
			//添加请求头
			String value;
			for (Entry<String, String> entry : headers.entrySet()) {
				value = entry.getValue();
				if (StringUtil.isNotNull(value)) {
					httpDelete.addHeader(entry.getKey(), value.trim());
				}
			}
		}
	}
	
	/**
	 * 获取请求结果
	 * 
	 * 场景：UTF-8文本
	 * 
	 * @param response
	 * 
	 * @return 如果返回200则获取响应体字符串；如果返回302则获取{status:"320",msg:"重定向页面",str:"Location头信息"}，调用者需要重定向根页面
	 * 
	 * @throws IOException
	 */
	private static String getResponse(CloseableHttpResponse response, String url)throws IOException {
		// 状态码
		int statusCode = response.getStatusLine().getStatusCode();
		switch (statusCode) {
		    case 200:
				// 返回响应内容
				return EntityUtils.toString(response.getEntity(), CharsetConstant.UTF8);
			case 302:
				// 重定向：320，根页面刷新
				return Result.redirectTop(response.getFirstHeader("location").getValue()).toJson();
		    default:
				throw RestRuntimeException.httpStatusError(url,statusCode);
		}
	}
	
	/**
	 * 获取请求结果
	 * 
	 * 场景：文件下载、非UTF-8文本
	 * 
	 * @param response
	 * 
	 * @return 如果返回200则获取响应体 byte[]
	 * 
	 * @throws IOException
	 */
	private static byte[] getResponseBytes(CloseableHttpResponse response, String url)throws IOException {
		int statusCode = response.getStatusLine().getStatusCode();
		if (statusCode == 200) {
			// 如果请求状态为200则返回响应内容
			return EntityUtils.toByteArray(response.getEntity());
		}else {
			throw RestRuntimeException.httpStatusError(url,statusCode);
		}
	}

	/**
	 * 组装URL
	 * 
	 * @param method
	 * @param url
	 * @param params
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年8月19日
	 */
	private static URI getUrl(String method, String url, Map<String, String> params) {
		//;
		URIBuilder builder;
		URI uri;
		try {
			builder = new URIBuilder(UrlUtil.trimHash(url));
			setParams(builder, params);
			uri = builder.build();
			LOG.info(HttpConstant.HTTP_SEND_LOG, method, uri);
			return uri;
		} catch (URISyntaxException e) {
			throw RestRuntimeException.httpError("URL格式错误：" + url, e);
		}
	}

}
